package zdream.control.desktop;

import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.utils.ObjectMap;

import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

import zdream.control.assets.FoeDescription;
import zdream.control.assets.TextureDescription;
import zdream.control.region.Region;
import zdream.control.world.Ticker;
import zdream.rockchronicle.core.graphics.SpritePainter;
import zdream.rockchronicle.core.textures.TextureManager;

/**
 * 启动位置在 "desktop" 目录
 *
 * @author Zdream
 * @date 2024-01-06
 * @since 0.0.1
 */
public class DesktopAssetsTester extends Game {
	int width = 1200, height = 672;
	PlayingScreen playingScreen;

	public static void main (String[] arg) {
		final DesktopAssetsTester tester = new DesktopAssetsTester();

		LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
		config.width = tester.width; // 25 * 48
		config.height = tester.height; // 14 * 48
		config.title = tester.getClass().getSimpleName();

		new LwjglApplication(tester, config);
	}

	@Override
	public void create() {
		this.setScreen(new ReadyScreen());
	}

	/**
	 * 测试使用的 screens
	 */
	class ReadyScreen implements Screen {
		OrthographicCamera camera;
		SpriteBatch batch;
		BitmapFont fontPressStart24; // 仅英文
		TextureManager textures = new TextureManager();

		long completedStartTime, switchDuration = 1000;

		{
			camera = new OrthographicCamera();
			camera.setToOrtho(false, width, height);

			batch = new SpriteBatch();
		}

		@Override
		public void show() {
			FreeTypeFontGenerator generator = new FreeTypeFontGenerator(Gdx.files.local("res/font/PressStart.ttf"));
			FreeTypeFontGenerator.FreeTypeFontParameter parameter = new FreeTypeFontGenerator.FreeTypeFontParameter();
			parameter.size = 24;
			parameter.characters = FreeTypeFontGenerator.DEFAULT_CHARS;
			fontPressStart24 = generator.generateFont(parameter);
			generator.dispose();

			textures.startLoading();
		}

		@Override
		public void render(float delta) {
			Gdx.gl.glClearColor(0, 0, 0, 1);
			Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
			camera.update();
			batch.setProjectionMatrix(camera.combined);

			// 检查资源加载情况
			int remain = textures.hasLoaded();

			batch.begin();
			fontPressStart24.draw(batch, "This is " + DesktopAssetsTester.class.getSimpleName() + ".", 100, 200);
			if (remain > 0) {
				fontPressStart24.draw(batch, "Loading assets: remain " + remain, 100, 160);
			} else {
				fontPressStart24.draw(batch, "Loading assets completed.", 100, 160);
			}
			batch.end();

			if (remain == 0 && completedStartTime == 0) {
				completedStartTime = System.currentTimeMillis();
			}

			if (completedStartTime == 0) {
				return;
			}

			long duration = System.currentTimeMillis() - completedStartTime;
			if (duration > switchDuration) {
				playingScreen = new PlayingScreen();
				playingScreen.textBatch = batch;
				playingScreen.textCamera = camera;
				playingScreen.fontPressStart24 = fontPressStart24;
				playingScreen.textures = textures;
				setScreen(playingScreen);
				dispose();
			}
		}

		@Override
		public void resize(int width, int height) {
		}

		@Override
		public void pause() {
		}

		@Override
		public void resume() {
		}

		@Override
		public void hide() {
		}

		@Override
		public void dispose() {
		}
	}

	/**
	 * 播放 assets 的 screen
	 */
	class PlayingScreen implements Screen {
		// 显示文字
		OrthographicCamera textCamera;
		SpriteBatch textBatch;
		BitmapFont fontPressStart24; // 仅英文

		// 显示图像
		OrthographicCamera spriteCamera;
		SpriteBatch spriteBatch;
		TextureManager textures = new TextureManager();

		SimpleSpritePainter painter = null;
		Ticker ticker = new Ticker();
		Inputs inputs = new Inputs();
		ShapeRenderer shapeRenderer = new ShapeRenderer();

		// cache
		String foeName;
		ObjectMap<String, FoeDescription> foeDescriptionMap = new ObjectMap<>();

		@Override
		public void show() {
			spriteBatch = new SpriteBatch();

			spriteCamera = new OrthographicCamera();
			spriteCamera.setToOrtho(false, width, height);

			spriteCamera.viewportWidth = Region.BLOCK_WIDTH;
			spriteCamera.viewportHeight = Region.BLOCK_HEIGHT;
			spriteCamera.position.set(
					spriteCamera.viewportWidth / 2.0f - 4,
					spriteCamera.viewportHeight / 2.0f - 4,
					0);

			textures.storage.allDescriptions().forEach(
					description -> foeDescriptionMap.put(description.name.toLowerCase(), description));
			Gdx.input.setInputProcessor(inputs);
		}

		private void readInputFromScanner() {
			if (!inputs.dirty) {
				return;
			}
			final String cmd = inputs.lastContent;
			inputs.dirty = false;

			if (cmd.startsWith("show ")) {
				String foeName = cmd.substring("show ".length()).trim().toLowerCase();
				final FoeDescription description = foeDescriptionMap.get(foeName);
				if (description == null) {
					System.err.println("未知的 FoeName: " + foeName);
					return;
				}
				showFoeTextures(description);
			} else if (cmd.startsWith("set ")) {
				String state = cmd.substring("set ".length()).trim();
				if (painter != null) {
					painter.setState(state);
				}
			} else if (cmd.startsWith("apply ")) {
				if (painter ==null) {
					return;
				}
				handleApply(cmd);
			} else if (cmd.startsWith("clear ")) {
				if (painter ==null) {
					return;
				}
				handleClear(cmd);
			} else if (cmd.trim().equals("exit")) {
				DesktopAssetsTester.this.dispose();
				System.exit(0);
			}
		}

		private void handleApply(String cmd) {
			String value = cmd.substring("apply ".length()).trim();

			switch (value) {
			case "flash":
				painter.applyFlash();
				break;
			case "red_stroke": case "stroke":
				painter.applyRedStroke();
				break;
			case "yellow_stroke":
				painter.applyYellowStroke();
				break;
			default:
				System.err.println("apply 命令错误: " + cmd);
				break;
			}
		}

		private void handleClear(String cmd) {
			String value = cmd.substring("clear ".length()).trim();

			if (value.equals("flash")) {
				painter.clearFlash();
			} else if (value.equals("red_stroke") || value.equals("yellow_stroke") || value.equals("stroke")) {
				painter.clearStroke();
			} else {
				System.err.println("apply 命令错误: " + cmd);
			}
		}

		@Override
		public void render(float delta) {
			Gdx.gl.glClearColor(0, 0.1f, 0.2f, 1);
			Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
			textCamera.update();
			textBatch.setProjectionMatrix(textCamera.combined);
			spriteCamera.update();
			spriteBatch.setProjectionMatrix(spriteCamera.combined);
			shapeRenderer.setProjectionMatrix(spriteCamera.combined);

			readInputFromScanner();
			ticker.runFrame(delta);

			textBatch.begin();
			drawTitle();
			textBatch.end();

			if (painter != null) {
				painter.draw(ticker);
			}

			drawGridLines();
		}

		private void drawTitle() {
			String text = String.format("PlayingScreen %s\ncommand: show, set, apply, clear" +
							"\n\n>>> %s\n<<< %s\n\ncurrent: %s\nstate: %s",
					DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalTime.now()),
					inputs.content, inputs.lastContent,
					painter == null ? "<empty>" : foeName,
					painter == null ? "none" : painter.getState());
			fontPressStart24.draw(textBatch, text, 20, height - 20);
		}

		private void drawGridLines() {
			shapeRenderer.begin(ShapeRenderer.ShapeType.Line);
			shapeRenderer.setColor(Color.DARK_GRAY);
			shapeRenderer.line(-5f, 1f, 25f, 1f);   // 横, y = 1
			shapeRenderer.line(-5f, -1f, 25f, -1f); // 横, y = -1
			shapeRenderer.line(1f, -5f, 1f, 5f);    // 竖, x = 1
			shapeRenderer.line(-1f, -5f, -1f, 5f);  // 竖, x = -1
			shapeRenderer.setColor(Color.WHITE);
			shapeRenderer.line(-5f, 0f, 25f, 0f); // 横
			shapeRenderer.line(0f, -5f, 0f, 5f);  // 竖
			shapeRenderer.end();
		}

		private void showFoeTextures(FoeDescription description) {
			if (painter != null) {
				painter.dispose();
			}

			FoeDescription.Painter painterDesc = description.painter;
			if (painterDesc == null || painterDesc.textures == null) {
				foeName = null;
				return;
			}

			painter = new SimpleSpritePainter(description.painter.textures);
			foeName = description.name;
			painter.batch = spriteBatch;
			painter.initTextures(textures);
		}

		@Override
		public void resize(int width, int height) {
		}

		@Override
		public void pause() {
		}

		@Override
		public void resume() {
		}

		@Override
		public void hide() {
		}

		@Override
		public void dispose() {
		}
	}

	public class SimpleSpritePainter extends SpritePainter {
		public SimpleSpritePainter(TextureDescription[] descriptions) {
			super(descriptions, playingScreen.ticker);
		}

		@Override
		public int zIndex() {
			return 100;
		}

		@Override
		public boolean getOrientation() {
			return true;
		}

		@Override
		public float getBx() {
			return 0;
		}

		@Override
		public float getBy() {
			return 0;
		}
	}

	static class Inputs implements InputProcessor {
		public String content = "";
		public String lastContent = "";
		public boolean dirty;

		@Override
		public boolean keyDown(int keycode) {
			return true;
		}

		@Override
		public boolean keyUp(int keycode) {
			return true;
		}

		@Override
		public boolean keyTyped(char character) {
			if (character == 8 && content.length() > 0) { // 退格
				content = content.substring(0, content.length() - 1);
			} else if (character == 13) { // 回车
				lastContent = content;
				content = "";
				dirty = true;
			} else if (character != 0) {
				content += character;
			}
			return true;
		}

		@Override
		public boolean touchDown(int screenX, int screenY, int pointer, int button) {
			return true;
		}

		@Override
		public boolean touchUp(int screenX, int screenY, int pointer, int button) {
			return true;
		}

		@Override
		public boolean touchDragged(int screenX, int screenY, int pointer) {
			return true;
		}

		@Override
		public boolean mouseMoved(int screenX, int screenY) {
			return true;
		}

		@Override
		public boolean scrolled(int amount) {
			return true;
		}
	}
}
